package com.cl.ks.service;

import com.alibaba.fastjson.JSONObject;
import com.cl.ks.config.ValueConfig;
import com.cl.ks.entity.*;
import com.cl.ks.repository.KsTableFieldRepository;
import com.cl.ks.repository.KsTableJoinRepository;
import com.cl.ks.repository.KsTableRepository;
import com.cl.ks.utils.DbUtil;
import com.jandar.pile.common.util.IParamBundle;
import com.jandar.pile.common.util.PageBundle;
import com.jandar.pile.common.util.ParamBundle;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.java.Log;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

import static com.cl.ks.constant.FieldConstants.PRIMARY_FIELD;
import static com.cl.ks.constant.QTableConstant.*;

/**
 * @author chenyitao
 * @date 2021/5/6
 */
@Service
@Log
public class KsTableService extends BaseManagerService {
    private final JPAQueryFactory queryFactory;

    private final KsTableRepository ksTableRepository;

    private final KsTableJoinRepository ksTableJoinRepository;

    private final KsTableFieldRepository ksTableFieldRepository;
    private final ValueConfig valueConfig;

    private final DbUtil dbUtil;

    @Data
    public static class Table {
        private Boolean update;

        private String tableName;

        private String tableAlias;

        private List<TableMapping> tableMappings;
    }

    @Data
    public static class TableMapping {
        private String tableName;

        private String tableAlias;

        private List<KsTableField> fieldMappingList;

        private JSONObject joinCondition;

        private String joinType;

        private Integer joinOrder;
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    public static class KsTableJoinEx extends KsTableJoin {
        private String joinConditionJson;
    }

    public KsTableService(JPAQueryFactory queryFactory, KsTableRepository ksTableRepository, KsTableJoinRepository ksTableJoinRepository, KsTableFieldRepository ksTableFieldRepository, ValueConfig valueConfig, DbUtil dbUtil) {
        this.queryFactory = queryFactory;
        this.ksTableRepository = ksTableRepository;
        this.ksTableJoinRepository = ksTableJoinRepository;
        this.ksTableFieldRepository = ksTableFieldRepository;
        this.valueConfig = valueConfig;
        this.dbUtil = dbUtil;
    }


    public ResponseEntity<Object> initTableMapping(Table table) {
        if (!Optional.ofNullable(table.getUpdate()).orElse(false)) {
            Long count = queryFactory.select(qKsTable.count()).from(qKsTable).where(qKsTable.tableName.eq(table.getTableName())).fetchOne();
            if (count > 0) {
                return ResponseEntity.ok().body(createMessage(false,
                        String.format("%s 已存在", table.getTableName())));
            }
        }
        // 主键字段数量
        long primaryCount = table.getTableMappings().stream()
                .map(tm -> tm.getFieldMappingList()
                        .stream()
                        .filter(f -> f.getTableFieldType().equals(PRIMARY_FIELD))
                        .count())
                .filter(c -> c > 1)
                .mapToLong(c -> c)
                .sum();
        // 存在单表主键大于0
        if (primaryCount > 0) {
            return ResponseEntity.ok().body(createMessage(false, "表主键数量错误"));
        }

        if (Optional.ofNullable(table.getUpdate()).orElse(false)) {
            tableDelete(table.getTableName());
        }

        KsTable ksTable = new KsTable();
        ksTable.setTableAlias(table.getTableAlias());
        // IsJoinTable -> true  表名不对应真实表名,根据 ks_table_join 查询真实表名
        // IsJoinTable -> false 表名对应真实表名
        ksTable.setTableName(table.getTableName());
        ksTable.setIsJoinTable(table.getTableMappings().size() != 1);
        try {
            ksTableRepository.save(ksTable);
        } catch (Exception e) {
            throw new RuntimeException("表映射信息创建失败");

        }

        if (ksTable.getIsJoinTable()) {
            table.getTableMappings().sort(Comparator.comparingInt(TableMapping::getJoinOrder));
            for (TableMapping tableMapping : table.getTableMappings()) {
                KsTableJoin ksTableJoin = new KsTableJoin();
                ksTableJoin.setJoinId(ksTable.getId());
                ksTableJoin.setJoinOrder(tableMapping.getJoinOrder());
                ksTableJoin.setJoinType(tableMapping.getJoinType());
                // 真实表名
                ksTableJoin.setTableName(tableMapping.getTableName());
                ksTableJoin.setTableAlias(tableMapping.getTableAlias());
                ksTableJoin.setJoinCondition(tableMapping.getJoinCondition().toJSONString());
                try {
                    ksTableJoinRepository.save(ksTableJoin);

                } catch (Exception e) {
                    throw new RuntimeException("关联表映射信息创建失败");
                }
                saveTableField(tableMapping.getFieldMappingList(), ksTable.getId());
            }
        } else {
            // 非多表关联
            TableMapping tableMapping = table.getTableMappings().get(0);
            saveTableField(tableMapping.getFieldMappingList(), 0);
        }
        return ResponseEntity.ok().body(createMessage(true, "表映射创建成功"));
    }

    private void saveTableField(List<KsTableField> ksTableFields, Integer joinId) {
        List<KsTableField> fieldRecords = new ArrayList<>();
        for (KsTableField ksTableField : ksTableFields) {
            KsTableField tableFieldRecord = new KsTableField();
            BeanUtils.copyProperties(ksTableField, tableFieldRecord);
            tableFieldRecord.setJoinId(joinId);
            fieldRecords.add(tableFieldRecord);
        }
        ksTableFieldRepository.saveAll(fieldRecords);
    }


    public ResponseEntity<Object> getOriginalTables() {
        List<String> tables = ksTableRepository.showTables();
        return ResponseEntity.ok().body(tables);
    }


    public ResponseEntity<Object> getMappingTables(Map<String, Object> param) {
        IParamBundle paramBundle = ParamBundle.newBundle(param);
        PageBundle pageBundle = paramBundle.getPageBundle();
        List<SysSourceMappingTable> ksTables = queryFactory.selectFrom(qSysSourceMappingTable).orderBy(qSysSourceMappingTable.id.desc()).fetch();
        Long count = queryFactory.select(qSysSourceMappingTable.count()).from(qSysSourceMappingTable).fetchFirst();
        JSONObject page = new JSONObject();
        page.put("list", ksTables);
        page.put("page", pageBundle.getPage());
        page.put("size", pageBundle.getSize());
        page.put("count", count);
        return ResponseEntity.ok().body(page);
    }


    public ResponseEntity<Object> getOriginalFields(String tableName) {
        List<Map<String, Object>> originalFields = dbUtil.fetchSQL(null,
                "SELECT COLUMN_NAME,COLUMN_TYPE FROM information_schema.columns " +
                        "WHERE TABLE_NAME = " + tableName + " AND TABLE_SCHEMA = " + valueConfig.getJooqOutput());
        List<JSONObject> resultFields = new ArrayList<>();
        for (Map<String, Object> originalField : originalFields) {
            JSONObject field = new JSONObject();
            field.put("fieldName", originalField.get("COLUMN_NAME"));
            field.put("fieldType", originalField.get("COLUMN_TYPE"));
            resultFields.add(field);
        }
        return ResponseEntity.ok().body(resultFields);
    }


    public ResponseEntity<Object> getMappingFields(String tableName, String dbCode) {
        BooleanExpression tableExpression = qSysSourceMappingTable.tableName.eq(tableName);
        BooleanExpression tableFieldExpression = qSysSourceMappingTableField.tableName.eq(tableName);
        if (dbCode == null) {
            tableExpression = tableExpression.and(qSysSourceMappingTable.dbCode.isNull());
            tableFieldExpression = tableFieldExpression.and(qSysSourceMappingTableField.dbCode.isNull());
        } else {
            tableExpression = tableExpression.and(qSysSourceMappingTable.dbCode.eq(dbCode));
            tableFieldExpression = tableFieldExpression.and(qSysSourceMappingTableField.dbCode.eq(dbCode));
        }
        SysSourceMappingTable sourceConfig = queryFactory.selectFrom(qSysSourceMappingTable).where(tableExpression).fetchFirst();
        if (sourceConfig == null) {
            return ResponseEntity.ok().body(createMessage(false, "无效 table"));
        }
        List<SysSourceMappingTableField> fields = queryFactory.selectFrom(qSysSourceMappingTableField).where(tableFieldExpression).fetch();
        return ResponseEntity.ok().body(fields);
    }


    public ResponseEntity<Object> deleteMappingTable(String tableName) {
        tableDelete(tableName);
        return ResponseEntity.ok().body(createMessage(true, "删除成功"));
    }


    public ResponseEntity<Object> getJoinTables(String tableName) {
        KsTable ksTable = queryFactory.selectFrom(qKsTable).where(qKsTable.tableName.eq(tableName)).fetchFirst();
        if (ksTable == null) {
            return ResponseEntity.ok().body(createMessage(false, "无效table"));
        }
        List<KsTableJoinEx> ksTableJoins = queryFactory.selectFrom(qKsTableJoin).where(qKsTableJoin.joinId.eq(ksTable.getId())).fetch()
                .stream().map(item -> {
                    KsTableJoinEx ksTableJoinEx = new KsTableJoinEx();
                    BeanUtils.copyProperties(item, ksTableJoinEx);
                    ksTableJoinEx.setJoinConditionJson(item.getJoinCondition().toString());
                    ksTableJoinEx.setJoinCondition(null);
                    return ksTableJoinEx;
                })
                .collect(Collectors.toList());
        return ResponseEntity.ok().body(ksTableJoins);
    }


    public ResponseEntity<Object> rlKsTableRules(String tableName) {
        List<KsRuleServiceOld.KsRuleEx> ksTables = queryFactory
                .selectFrom(qKsRule)
                .innerJoin(qRlRuleTable)
                .on(qRlRuleTable.ruleId.eq(qKsRule.id))
                .where(qRlRuleTable.tableName.eq(tableName))
                .fetch()
                .stream()
                .map(item -> {
                    KsRuleServiceOld.KsRuleEx ksRuleEx = new KsRuleServiceOld.KsRuleEx();
                    BeanUtils.copyProperties(item, ksRuleEx);
                    ksRuleEx.setRuleExpressionJsonStr(ksRuleEx.getRuleExpressionJson());
                    ksRuleEx.setRuleExpressionJson(null);
                    return ksRuleEx;
                })
                .collect(Collectors.toList());
        return ResponseEntity.ok().body(ksTables);
    }


    @Transactional
    public void tableDelete(String tableName) {
        KsTable ksTable = queryFactory.selectFrom(qKsTable).where(qKsTable.tableName.eq(tableName)).fetchFirst();
        if (ksTable == null) {
            return;
        }
        queryFactory.delete(qKsTable).where(qKsTable.tableName.eq(tableName)).execute();
        if (ksTable.getIsJoinTable()) {
            queryFactory.delete(qKsTableField).where(qKsTableField.joinId.eq(ksTable.getId())).execute();
            queryFactory.delete(qKsTableJoin).where(qKsTableJoin.joinId.eq(ksTable.getId())).execute();
        } else {
            queryFactory.delete(qKsTableField).where(qKsTableField.tableName.eq(tableName).and(qKsTableField.joinId.eq(0))).execute();
        }
    }
}
